package com.nervenets.web.controller.order;

import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.nervenets.Global;
import com.nervenets.utils.*;
import com.nervenets.web.controller.BaseController;
import com.nervenets.web.controller.IdTokenParams;
import com.nervenets.web.controller.TokenParams;
import com.nervenets.web.entity.KeyValue;
import com.nervenets.web.entity.MessageCode;
import com.nervenets.web.entity.ResponseResult;
import com.nervenets.web.enumeration.*;
import com.nervenets.web.hibernate.dao.*;
import com.nervenets.web.hibernate.domain.*;
import com.nervenets.web.rabbitmq.FlowMessage;
import com.nervenets.web.rabbitmq.FlowType;
import com.nervenets.web.redis.domain.user.UserStored;
import com.nervenets.web.redis.utils.RedisSyncLocker;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import java.math.BigInteger;
import java.util.*;

@Log4j
@Api(value = "订单接口v1", description = "订单相关接口")
@RestController
@RequestMapping("/api/v1/order")
public class OrderController extends BaseController {
    @Resource
    private OrderDao orderDao;
    @Resource
    private UserDao userDao;
    @Resource
    private AddressDao addressDao;
    @Resource
    private RedisSyncLocker redisSyncLocker;
    @Resource
    private StatisticsOfOrderDao statisticsOfOrderDao;

    @ApiOperation("新建订单")
    @PostMapping("/create")
    public ResponseResult create(@RequestBody OrderCreateParams params, HttpServletRequest request) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);

        final Set<ConstraintViolation<OrderCreateParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }

        final UserStored user = globalService.getUser(params.resolveUserId());
        if (null == user) return errorMessage(MessageCode.code_777);
        final Address address = addressDao.findById(params.getAddressId()).orElse(null);
        if (null == address) return errorMessage(MessageCode.code_404, "用户地址ID不正确");

        final String cartLockedKey = String.format("CART%s", params.resolveUserId());
        ShopCartContainer cartContainer = new ShopCartContainer();
        final String redisValue = globalService.getGlobalPair(cartLockedKey);
        if (!StringUtils.isEmpty(redisValue)) {
            try {
                cartContainer = JSONObject.parseObject(redisValue, ShopCartContainer.class);
            } catch (Exception e) {
                //
            }
        }
        if (cartContainer.getShopCarts().isEmpty()) return errorMessage(MessageCode.code_444, "您选择的服务项目空空如也~~");
        List<OrderItem> orderItems = new ArrayList<>();
        cartContainer.getShopCarts().forEach(cart -> {
            cart.getItems().forEach(item -> {
                if (item.getTotal() > 0) {
                    orderItems.add(new OrderItem(cart.getTypeId(), cart.getTypeName(), item.getProductId(), item.getLogo(), item.getName(), item.getPrice(), item.getTotal(), ""));
                }
            });
        });

        Order order = orderDao.findTop1ByUserIdAndStatus(params.resolveUserId(), OrderStatus.init);
        if (null == order) order = new Order();
        order.setUserId(params.resolveUserId());
        order.setPayType(params.getPayType());
        order.addFlow(null, OrderStatus.init, "提交成功，等待用户支付", false);

        order.setCreateTime(JodaUtils.getTimestamp());

        orderDao.save(order);

        //if (payMoney > 0) {
        switch (params.getPayType()) {
            case surplus:
                final long surplus = globalService.getUserWealthOfMoney(params.resolveUserId(), WealthType.money);
                if (surplus < order.getPayMoney()) {
                    return errorMessage(MessageCode.code_410);
                }
                order.setStatus(OrderStatus.paid);
                orderDao.save(order);

                messageProducer.flow(new FlowMessage(user.getUserId(), FlowType.orderPaid, HttpTools.getIp(request), order.getId()));
                break;
            case wxpay:
                final String notifyUrl = String.format("https://%s/api/v1/activity/orderWxCallback", Global.getApplicationProperties().getProperty("app.base.domain"));
                final WxPayMpOrderResult wxPayAppOrderResult = wxPay("洗护服务", order.orderNumber(), order.getPayMoney() - params.getDeduction(), user.getOpenId(), notifyUrl, request);
                orderDao.save(order);

                return successMessage(wxPayAppOrderResult, new KeyValue("extra", order));
            case alipay:
                break;
        }
        /*} else {
            messageProducer.flow(new FlowMessage(user.getUserId(), FlowType.orderPaid, HttpTools.getIp(request), order.getId()));
        }*/
        return successMessage(null, new KeyValue("extra", order));
    }

    @ApiOperation("订单再次支付")
    @PostMapping("/pay")
    public ResponseResult pay(@RequestBody IdTokenParams params, HttpServletRequest request) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);

        final Set<ConstraintViolation<IdTokenParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }

        final UserStored user = globalService.getUser(params.resolveUserId());
        if (null == user) return errorMessage(MessageCode.code_777);

        final Order order = orderDao.findById(params.getTargetId()).orElse(null);
        if (null == order) return errorMessage(MessageCode.code_404, "您要支付的订单不存在");
        order.setCreateTime(JodaUtils.getTimestamp());
        orderDao.save(order);

        switch (order.getPayType()) {
            case surplus:
                final long surplus = globalService.getUserWealthOfMoney(params.resolveUserId(), WealthType.money);
                if (surplus < order.getPayMoney()) {
                    return errorMessage(MessageCode.code_410);
                }
                order.setStatus(OrderStatus.paid);
                orderDao.save(order);

                messageProducer.flow(new FlowMessage(order.getUserId(), FlowType.orderPaid, HttpTools.getIp(request), order.getId()));
                break;
            case wxpay:
                final String notifyUrl = String.format("https://%s/api/v1/activity/orderWxCallback", Global.getApplicationProperties().getProperty("app.base.domain"));
                final WxPayMpOrderResult wxPayAppOrderResult = wxPay("洗护服务", order.orderNumber(), order.getPayMoney() , user.getOpenId(), notifyUrl, request);
                orderDao.save(order);

                return successMessage(wxPayAppOrderResult, new KeyValue("extra", order));
            case alipay:
                break;
        }

        return errorMessage(MessageCode.code_444, "参数错误");
    }

    @ApiOperation("订单统计")
    @PostMapping("/counts")
    public ResponseResult counts(@RequestBody TokenParams params) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);

        final List<Map> maps = orderDao.getCountGroupByStatus();
        JSONObject object = new JSONObject();
        Long total = 0L;
        for (Map map : maps) {
            Integer status = (Integer) map.get("status");
            BigInteger t = (BigInteger) map.get("total");
            total += t.longValue();
            final OrderStatus orderStatus = OrderStatus.values()[status];
            object.put(orderStatus.name(), t);
        }
        object.put("total", total);

        return successMessage(object);
    }

    @ApiOperation("订单列表")
    @PostMapping("/list")
    public ResponseResult list(@RequestBody OrderListPagingParams params) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);

        final Set<ConstraintViolation<OrderListPagingParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }

        Specification<Order> specification = (Specification<Order>) (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (null != params.getStatus()) {
                predicates.add(criteriaBuilder.equal(root.get("status"), params.getStatus()));
            }
            if (params.getStartTime() > 0 && params.getEndTime() > 0 && params.getEndTime() > params.getStartTime()) {
                predicates.add(criteriaBuilder.between(root.get("createTime"), params.getStartTime(), params.getEndTime()));
            }
            if (params.getTargetUserId() > 0) {
                predicates.add(criteriaBuilder.equal(root.get("userId"), params.getTargetUserId()));
            }
            if (params.getStartPayTime() > 0 && params.getEndPayTime() > 0 && params.getEndPayTime() > params.getStartPayTime()) {
                predicates.add(criteriaBuilder.between(root.get("payTime"), params.getStartPayTime(), params.getEndPayTime()));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        final Page<Order> pages = orderDao.findAll(specification, PageRequest.of(params.getPage(), params.getLimit(), new Sort(Sort.Direction.DESC, "createTime")));
        List<Order> list = new ArrayList<>();
        HashMap<Long, UserStored> userStoredHashMap = new HashMap<>();
        HashMap<Long, Long> surplusHashMap = new HashMap<>();
        pages.map(order -> {
            list.add(order);
            if (!userStoredHashMap.containsKey(order.getUserId()) && order.getUserId() > 0) {
                userStoredHashMap.put(order.getUserId(), globalService.getUser(order.getUserId()));
                surplusHashMap.put(order.getUserId(), globalService.getUserWealthOfMoney(order.getUserId(), WealthType.money));
            }
            return order;
        });
        return successMessage(list
                , new KeyValue("page", params.getPage())
                , new KeyValue("total", pages.getTotalElements())
                , new KeyValue("users", userStoredHashMap)
                , new KeyValue("surplus", surplusHashMap)
        );
    }

    @ApiOperation("订单删除")
    @PostMapping("/del")
    public ResponseResult del(@RequestBody IdTokenParams params) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);

        final Set<ConstraintViolation<IdTokenParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }
        final Order object = orderDao.findById(params.getTargetId()).orElse(null);
        if (null == object) return errorMessage(MessageCode.code_404, "您操作的订单不存在");

        object.setRemoved(JodaUtils.getTimestamp());
        orderDao.save(object);

        return successMessage();
    }

    @ApiOperation("查看订单详情")
    @PostMapping("/detail")
    public ResponseResult detail(@RequestBody IdTokenParams params) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);

        final Set<ConstraintViolation<IdTokenParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }
        final Order object = orderDao.findById(params.getTargetId()).orElse(null);
        if (null == object) return errorMessage(MessageCode.code_404, "您查看的订单不存在");

        final UserStored orderUser = globalService.getUser(object.getUserId());

        return successMessage(object
                , new KeyValue("orderUser", orderUser)
        );
    }

    @ApiOperation("订单取消")
    @PostMapping("/cancel")
    public ResponseResult cancel(@RequestBody OrderCancelParams params) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);

        final Set<ConstraintViolation<OrderCancelParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }
        return (ResponseResult) redisSyncLocker.idLock(params.getOrderId(), o -> {
            final Order object = orderDao.findById(params.getOrderId()).orElse(null);
            if (null == object) return errorMessage(MessageCode.code_404, "您取消的订单不存在");

            if (OrderStatus.cancel.equals(object.getStatus())) return errorMessage(MessageCode.code_444, "该订单已取消");
            if (object.getStatus().ordinal() > OrderStatus.paid.ordinal())
                return errorMessage(MessageCode.code_444, "已经进入处理流程的订单无法取消咯");

            final boolean needRefund = OrderStatus.paid.equals(object.getStatus());
            if (needRefund) {
                switch (object.getPayType()) {
                    case surplus:
                        return errorMessage(MessageCode.code_444, "暂时不支持余额抵扣");
                    case wxpay:
                        final WxPayRefundResult wxPayRefundResult = wxRefund(object.orderNumber(), Math.toIntExact(object.getPayMoney()));
                        if (!("SUCCESS".equals(wxPayRefundResult.getReturnCode()) && "SUCCESS".equals(wxPayRefundResult.getResultCode()))) {
                            return errorMessage(MessageCode.code_444, "退款失败，无法取消，原因：" + wxPayRefundResult.getErrCodeDes());
                        }
                        break;
                    case alipay:
                        return errorMessage(MessageCode.code_444, "暂时不支持支付宝支付");
                }
            }

            object.addFlow(object.getStatus(), OrderStatus.cancel, String.format("订单取消，原因：%s", params.getReason()), false);
            object.setStatus(OrderStatus.cancel);
            orderDao.save(object);

            statisticsOfOrderDao.updateCount(0, 0, 0, 1, 0, JodaUtils.getStartOfToday());

            return successMessage();
        });
    }

    @ApiOperation("订单送件成功")
    @PostMapping("/finished")
    public ResponseResult finished(@RequestBody OrderFinishedParams params) throws Exception {
        if (params.tokenIllegal()) return errorMessage(MessageCode.code_777);

        final Set<ConstraintViolation<OrderFinishedParams>> errors = validator.validate(params);
        if (!errors.isEmpty()) {
            return errorMessage(errors);
        }
        return (ResponseResult) redisSyncLocker.idLock(params.getOrderId(), o -> {
            final Order object = orderDao.findById(params.getOrderId()).orElse(null);
            if (null == object) return errorMessage(MessageCode.code_404, "您选择的订单不存在");

            if (object.getStatus().ordinal() >= OrderStatus.finished.ordinal()) {
                return errorMessage(MessageCode.code_444, "该订单已确认处理完成，无法再次操作");
            }

            object.addFlow(object.getStatus(), OrderStatus.finished, "订单标记完成", false);
            object.setStatus(OrderStatus.finished);
            orderDao.save(object);

            statisticsOfOrderDao.updateCount(0, 0, 0, 1, 0, JodaUtils.getStartOfToday());

            return successMessage();
        });
    }
}
